<template>
    <div class="camera_outer">
        <video id="videoCamera" :width="videoWidth" :height="videoHeight" autoplay v-show="isCameraActive"></video>
        <canvas id="canvasCamera" style="display:none;" :width="videoWidth" :height="videoHeight"></canvas>

        <!-- 摄像头状态提示 -->
        <div v-if="errorMsg" class="error-tip">
            <el-alert :title="errorMsg" type="error" show-icon></el-alert>
            <div class="solution-tips">
                <p>可能的解决方案：</p>
                <ul>
                    <li>检查摄像头是否被其他程序占用</li>
                    <li>在浏览器设置中允许摄像头权限</li>
                    <li>尝试使用其他浏览器（推荐Chrome）</li>
                    <li>检查设备是否连接了摄像头</li>
                </ul>
            </div>
        </div>

        <div style="display: flex;justify-content: space-between;align-items: center; margin-top: 20px">
            <el-button type="primary" @click="setImage()" :disabled="!isCameraActive">拍照</el-button>
            <el-button @click="getCompetence()" :disabled="isCameraActive">
                {{ isCameraActive ? '摄像头已开启' : '打开摄像头' }}
            </el-button>
            <el-button v-if="isCameraActive" @click="closeCamera">关闭</el-button>
        </div>
    </div>
</template>
<script>
    import request from "../utils/request.js";
    export default {
        data() {
            return {
                videoWidth: 500,
                videoHeight: 500,
                isCameraActive: false,
                errorMsg: "",
                stream: null,
                imgSrc: '',
                thisCancas: null,
                thisContext: null,
                thisVideo: null,
            }
        },
        mounted() {
            // this.getCompetence()
        },
        destroyed() {
            this.stopNavigator();
        },
        methods: {
            handleAvatarSuccess(res, file) {
                this.$emit('refreshDataList', res.data.url) // 触发自定义事件
                this.closeCamera() // 关闭摄像头
            },
            async checkCameraSupport() {
                if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
                    this.errorMsg = "您的浏览器不支持摄像头功能";
                    return false;
                }

                try {
                    const devices = await navigator.mediaDevices.enumerateDevices();
                    const hasCamera = devices.some(device => device.kind === 'videoinput');
                    if (!hasCamera) {
                        this.errorMsg = "未检测到可用摄像头设备";
                        return false;
                    }
                    return true;
                } catch (e) {
                    this.errorMsg = "检测摄像头设备时出错：" + e.message;
                    return false;
                }
            },

            // 打开摄像头
            async getCompetence() {
                // 重置错误状态
                this.errorMsg = "";

                // 检查设备支持
                if (!(await this.checkCameraSupport())) {
                    return;
                }

                try {
                    // 停止已存在的流
                    this.stopNavigator();

                    // 初始化视频元素
                    this.thisCancas = document.getElementById('canvasCamera');
                    this.thisContext = this.thisCancas.getContext('2d');
                    this.thisVideo = document.getElementById('videoCamera');

                    // 配置摄像头参数
                    const constraints = {
                        audio: false,
                        video: {
                            width: { ideal: this.videoWidth },
                            height: { ideal: this.videoHeight },
                            facingMode: 'user', // 优先使用前置摄像头
                            // transform: 'scaleX(-1)' // 可能导致某些设备出错，暂时移除
                        }
                    };

                    // 获取媒体流
                    const stream = await navigator.mediaDevices.getUserMedia(constraints);
                    this.stream = stream; // 保存媒体流

                    // 现代浏览器使用 srcObject
                    if ('srcObject' in this.thisVideo) {
                        this.thisVideo.srcObject = stream;
                    } else {
                        // 兼容旧版本浏览器
                        this.thisVideo.src = window.URL.createObjectURL(stream);
                    }

                    // 视频加载成功后播放
                    this.thisVideo.onloadedmetadata = () => {
                        this.thisVideo.play();
                        this.isCameraActive = true;
                    };

                } catch (err) {
                    console.error("摄像头错误:", err);
                    this.handleCameraError(err);
                }
            },

            // 错误处理
            handleCameraError(err) {
                this.isCameraActive = false;

                switch(err.name) {
                    case 'NotAllowedError':
                        this.errorMsg = "用户拒绝了摄像头访问权限";
                        break;
                    case 'NotFoundError':
                        this.errorMsg = "未找到可用的摄像头设备";
                        break;
                    case 'NotReadableError':
                        this.errorMsg = "摄像头设备已被占用";
                        break;
                    case 'OverconstrainedError':
                        this.errorMsg = "无法满足摄像头配置要求";
                        break;
                    default:
                        this.errorMsg = `摄像头初始化失败: ${err.message || err.name}`;
                }
            },

            // 关闭摄像头
            closeCamera() {
                this.stopNavigator();
                this.isCameraActive = false;
                this.$emit('close');
            },

            // 安全停止媒体流
            stopNavigator() {
                if (this.stream) {
                    this.stream.getTracks().forEach(track => {
                        track.stop();
                    });
                    this.stream = null;
                }

                if (this.thisVideo) {
                    this.thisVideo.srcObject = null;
                    this.thisVideo.src = "";
                }
            },
            //  绘制图片（拍照功能）
            // 修改后的 setImage 方法
            setImage() {
                var _this = this
                _this.thisContext.drawImage(_this.thisVideo, 0, 0, _this.videoWidth, _this.videoHeight)
                var image = this.thisCancas.toDataURL('image/png')
                _this.imgSrc = image

                // 转换base64为File对象
                const fileName = (new Date()).valueOf()
                const file = this.base64ToFile(image, fileName)  // 直接使用转换后的File对象

                // 创建FormData并添加文件
                const formData = new FormData()
                formData.append('file', file) // 字段名必须与接口参数名一致

                // 发送请求（根据实际请求库调整）
                request.post("/files/upload", formData).then(res => {
                    if (res !== null && res !== "") {
                        this.$message.success('上传成功');
                        this.$emit('refreshDataList', res)
                    }
                }).catch(err => {
                    console.error('上传失败:', err)
                })
            },
            // base64图片转file的方法（base64图片, 设置生成file的文件名）
            base64ToFile(base64, fileName) {
                // 将base64按照 , 进行分割 将前缀  与后续内容分隔开
                const data = base64.split(',')
                // 利用正则表达式 从前缀中获取图片的类型信息（image/png、image/jpeg、image/webp等）
                const type = data[0].match(/:(.*?);/)[1]
                // 从图片的类型信息中 获取具体的文件格式后缀（png、jpeg、webp）
                const suffix = type.split('/')[1]
                // 使用atob()对base64数据进行解码  结果是一个文件数据流 以字符串的格式输出
                const bstr = window.atob(data[1])
                // 获取解码结果字符串的长度
                let n = bstr.length
                // 根据解码结果字符串的长度创建一个等长的整形数字数组
                // 但在创建时 所有元素初始值都为 0
                const u8arr = new Uint8Array(n)
                // 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
                while (n--) {
                    // charCodeAt()：获取给定索引处字符对应的 UTF-16 代码单元
                    u8arr[n] = bstr.charCodeAt(n)
                }
                // 利用构造函数创建File文件对象
                // new File(bits, name, options)
                const file = new File([u8arr], `${fileName}.${suffix}`, {
                    type: type
                })
                // 将File文件对象返回给方法的调用者
                return file
            },
            // closeCamera() {
            //     this.stopNavigator()
            //     this.$emit('close') // 触发关闭事件
            // },
            // // 关闭摄像头
            // stopNavigator() {
            //     this.thisVideo.srcObject.getTracks()[0].stop()
            // }
        }
    }
</script>
<style lang="less" scoped>
    .camera_outer {
        position: relative;
        overflow: hidden;
        // background: url("../../assets/img/user_0608_04.png") no-repeat center;
        background-size: 100%;
        video,
        canvas,
        .tx_img {
            -moz-transform: scaleX(-1);
            -webkit-transform: scaleX(-1);
            -o-transform: scaleX(-1);
            transform: scaleX(-1);
        }
        .btn_camera {
            position: absolute;
            bottom: 4px;
            left: 0;
            right: 0;
            height: 50px;
            background-color: rgba(0, 0, 0, 0.3);
            line-height: 50px;
            text-align: center;
            color: #ffffff;
        }
        .bg_r_img {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            top: 0;
        }
        .img_bg_camera {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            top: 0;
            img {
                width: 300px;
                height: 300px;
            }
            .img_btn_camera {
                position: absolute;
                bottom: 0;
                left: 0;
                right: 0;
                height: 50px;
                line-height: 50px;
                text-align: center;
                background-color: rgba(0, 0, 0, 0.3);
                color: #ffffff;
                .loding_img {
                    width: 50px;
                    height: 50px;
                }
            }
        }
    }
</style>
